﻿using System.Collections;
using System.Reflection;

namespace Mes.Application.Services.MockData
{
    public class MockDataService
    {
        public T Mock<T>(Func<string, object> handler = null)
        {
            var rnd = new Random();
            var tRetn = Activator.CreateInstance<T>();
            FillProperties(tRetn, "", rnd, handler);

            return tRetn;
        }

        private object OnGlobalHandler(string propPath)
        {
            return null;
        }

        private object HandlerExecute(Func<string, object> handler, string propPath)
        {
            var pVal = handler?.Invoke(propPath) ?? OnGlobalHandler(propPath);
            return pVal;
        }

        private void FillProperties(object obj, string propPath, Random rnd, Func<string, object> handler)
        {
            var bfs = BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.FlattenHierarchy;
            var pis = obj.GetType().GetProperties(bfs);

            foreach (var pi in pis)
            {
                var pType = pi.PropertyType;
                var pVal = MakeValue(pType, pi.Name, propPath + "." + pi.Name, rnd, handler);
                pi.SetValue(obj, pVal);
            }
        }

        private object MakeValue(Type type, string parentTypeName, string propPath, Random rnd, Func<string, object> handler)
        {
            if (type == typeof(string))
            {
                return HandlerExecute(handler, propPath) ?? $"{parentTypeName} {rnd.Next(1, 10)}";
            }
            else if (type == typeof(bool))
            {
                return HandlerExecute(handler, propPath) ?? rnd.Next(0, 2) != 0;
            }
            else if (type.IsEnum)
            {
                var kvs = Enum.GetValues(type);
                var eVal = System.Convert.ChangeType(kvs.GetValue(rnd.Next(kvs.Length)), type);
                return HandlerExecute(handler, propPath) ?? eVal;
            }
            else if (type == typeof(int) || type == typeof(uint) ||
                     type == typeof(long) || type == typeof(ulong) ||
                     type == typeof(byte) || type == typeof(sbyte) ||
                     type == typeof(short) || type == typeof(ushort))
            {
                var val = rnd.Next(1, 256);
                return HandlerExecute(handler, propPath) ?? System.Convert.ChangeType(val, type);
            }
            else if (type == typeof(float) || type == typeof(double) ||
                     type == typeof(decimal))
            {
                var val = rnd.NextDouble() + rnd.Next(1, 100);
                return HandlerExecute(handler, propPath) ?? System.Convert.ChangeType(val, type);
            }
            else if (type == typeof(DateTime))
            {
                return HandlerExecute(handler, propPath) ?? DateTime.Now;
            }
            else if (type.IsClass)
            {
                var pObj = Activator.CreateInstance(type);
                FillProperties(pObj, propPath, rnd, handler);
                return pObj;
            }
            else if (IsAssignableToGenericType(type, typeof(Nullable<>), out var gtArgType1))
            {
                var nVal = MakeValue(gtArgType1, parentTypeName, propPath + "." + parentTypeName, rnd, handler);
                var rVal = Activator.CreateInstance(typeof(Nullable<>).MakeGenericType(gtArgType1), nVal);
                return rVal;
            }
            else if (IsAssignableToGenericType(type, typeof(IEnumerable<>), out var gtArgType2))
            {
                var lst = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(gtArgType2));
                var lstLen = rnd.Next(1, 6);
                for (var i = 0; i < lstLen; ++i)
                {
                    object gtArgObj = null;
                    try
                    {
                        gtArgObj = MakeValue(gtArgType2, parentTypeName, propPath, rnd, handler);
                    }
                    catch
                    {
                        throw;
                    }
                    if (gtArgObj != null)
                    {
                        lst.Add(gtArgObj);
                    }
                }
                return lst;
            }
            else
            {
                throw new InvalidOperationException($"{nameof(MakeValue)} 未预期的类型：{type.FullName}");
            }
        }

        private static bool IsAssignableToGenericType(Type givenType, Type genericType, out Type gtArgType)
        {
            gtArgType = null;

            if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            {
                gtArgType = givenType.GetGenericArguments()[0];
                return true;
            }

            var interfaceTypes = givenType.GetInterfaces();
            foreach (var it in interfaceTypes)
            {
                if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
                {
                    gtArgType = it.GetGenericArguments()[0];
                    return true;
                }
            }

            var baseType = givenType.BaseType;
            if (baseType == null)
                return false;

            return IsAssignableToGenericType(baseType, genericType, out gtArgType);
        }
    }
}
